package ltd.bugs.cute.httpclient;

import javax.net.ssl.SSLContext;

import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;

import ltd.bugs.cute.common.spring.ApplicationContextHolder;
import ltd.bugs.cute.httpclient.config.HttpClientConfig;

public class HttpComponentsRequestFactory {

  private static HttpRequestRetryHandler retryHandler = HttpRequestRetryHandlerBuilder.build();
  private static HttpClientConfig httpClientConfig =
      ApplicationContextHolder.getBean(HttpClientConfig.class);
  // http连接池管理器
  private static PoolingHttpClientConnectionManager httpClientConnectionManager;
  private static RequestConfig requestConfig;

  /**
   * 私有构造器，不允许外部创建
   */
  private HttpComponentsRequestFactory() {

  }

  // 静态快初始化连接管理器
  static {
    // 解决证书问题PKIX path building failed:
    // sun.security.provider.certpath.SunCertPathBuilderException:
    // unable to find valid certification path to requested target
    SSLContext sslContext = null;
    try {
      sslContext = SSLContexts.custom().loadTrustMaterial((chain, authType) -> true).build();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext);
    Registry<ConnectionSocketFactory> socketFactoryRegistry =
        RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslFactory)
            .register("http", PlainConnectionSocketFactory.getSocketFactory()).build();

    // 设置请求时候的参数
    requestConfig = RequestConfig.custom()
        .setConnectionRequestTimeout(httpClientConfig.getConnectionRequestTimeout())
        .setSocketTimeout(httpClientConfig.getSocketTimeout())
        .setConnectTimeout(httpClientConfig.getConnectionTimeout()).build();

    httpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    httpClientConnectionManager.setMaxTotal(httpClientConfig.getMaxConnection());
    httpClientConnectionManager.setDefaultMaxPerRoute(httpClientConfig.getMaxConnectionPerRoute());
  }

  public static CloseableHttpClient getHttpClient() throws Exception {
    return HttpClients.custom().setConnectionManager(httpClientConnectionManager)
        .setRetryHandler(retryHandler).setDefaultRequestConfig(requestConfig).build();
  }

  public static PoolingHttpClientConnectionManager getConnectionManager() {
    return httpClientConnectionManager;
  }
}
